package com.autumn.util;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

/**
 * 包帮助
 *
 * @author 杨昌国
 * @date 2017-06-06 16:00:04
 */
public class PackageUtils {
	static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

	/**
	 * 获取某包下的目标类型
	 * 
	 * @param packageName
	 *            包名
	 * @param targetClass
	 *            目标类型
	 * @param isIncludeInterface
	 *            是否包含接口
	 * @param isIncludeAbstract
	 *            是否包含抽象
	 * @param isIncludePrivate
	 *            是否包含私有
	 * @return 类集
	 * @throws IOException
	 */
	public static Set<Class<?>> getPackageTargetClass(String packageName, Class<?> targetClass,
			boolean isIncludeInterface, boolean isIncludeAbstract, boolean isIncludePrivate) throws IOException {
		if (targetClass == null) {
			throw new NullPointerException("targetClass 为 null");
		}
		Set<String> className = getClassName(packageName);
		Set<Class<?>> types = new HashSet<Class<?>>();
		for (String name : className) {
			try {
				Class<?> type = Class.forName(name);
				if (targetClass.isAssignableFrom(type)) {
					int mod = type.getModifiers();
					if (!isIncludeInterface && Modifier.isInterface(mod)) {
						continue;
					}
					if (!isIncludeAbstract && Modifier.isAbstract(mod)) {
						continue;
					}
					if (!isIncludePrivate && !Modifier.isPublic(mod)) {
						continue;
					}
					types.add(type);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return types;
	}

	/**
	 * 获取某包下特定注解的所有类型
	 * 
	 * @param packageName
	 *            包名称
	 * @param annotationClass
	 *            注解类型
	 * @param isIncludeInterface
	 *            包含接口
	 * @param isIncludeAbstract
	 *            包含抽象
	 * @param isIncludePrivate
	 *            包含私有
	 * @return
	 * @throws IOException
	 */
	public static Set<Class<?>> getPackageAnnotationClass(String packageName,
			Class<? extends Annotation> annotationClass, boolean isIncludeInterface, boolean isIncludeAbstract,
			boolean isIncludePrivate) throws IOException {
		if (annotationClass == null) {
			throw new NullPointerException("annotationClass 为 null");
		}
		Set<String> className = getClassName(packageName);
		Set<Class<?>> types = new HashSet<Class<?>>();
		for (String name : className) {
			try {
				Class<?> type = Class.forName(name);
				if (TypeUtils.isAnnotationClass(type, annotationClass)) {
					int mod = type.getModifiers();
					if (!isIncludeInterface && Modifier.isInterface(mod)) {
						continue;
					}
					if (!isIncludeAbstract && Modifier.isAbstract(mod)) {
						continue;
					}
					if (!isIncludePrivate && !Modifier.isPublic(mod)) {
						continue;
					}
					types.add(type);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return types;
	}

	/**
	 * 获取某包下特定注解的所有类型
	 * 
	 * @param packageName
	 *            包名
	 * @param annotationClass
	 *            注解类型
	 * @return 类集
	 * @throws IOException
	 */
	public static Set<Class<?>> getPackageAnnotationClass(String packageName,
			Class<? extends Annotation> annotationClass) throws IOException {
		if (annotationClass == null) {
			throw new NullPointerException("annotationClass 为 null");
		}
		Set<String> className = getClassName(packageName);
		Set<Class<?>> types = new HashSet<Class<?>>();
		for (String name : className) {
			try {
				Class<?> type = Class.forName(name);
				if (TypeUtils.isAnnotationClass(type, annotationClass)) {
					types.add(type);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return types;
	}

	/**
	 * 获取某包下所有类型
	 * 
	 * @param packageName
	 *            包名
	 * @return 类集
	 * @throws IOException
	 */
	public static Set<Class<?>> getPackageClass(String packageName) throws IOException {
		Set<String> className = getClassName(packageName);
		Set<Class<?>> types = new HashSet<Class<?>>();
		for (String name : className) {
			try {
				types.add(Class.forName(name));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return types;
	}

	/**
	 * 获取某包下所有类
	 * 
	 * @param packageName
	 *            包名
	 * @param childPackage
	 *            是否遍历子包
	 * @return 类列表
	 * @throws IOException
	 */
	public static Set<String> getClassName(String packageName) throws IOException {
		if (packageName == null || packageName.trim().length() == 0) {
			throw new NullPointerException("packageName 为 null 或空字符串");
		}
		Set<String> fileNames = new HashSet<String>();
		ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
		MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
		String fullPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
				+ ClassUtils.convertClassNameToResourcePath(packageName) + "/" + DEFAULT_RESOURCE_PATTERN;
		Resource[] resources = resolver.getResources(fullPackage);
		if (resources != null && resources.length > 0) {
			for (Resource resource : resources) {
				if (resource.isReadable()) {
					MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
					if (metadataReader != null) {
						fileNames.add(metadataReader.getClassMetadata().getClassName());
					}
				}
			}
		}
		return fileNames;
	}

}
